﻿using GsOrganization.Common.Extend;
using GsOrganization.Common.Helper;
using GsOrganization.TcpPacket;
using System.Net.Sockets;
using System.Runtime.Serialization;

namespace GsOrganization.TcpServer
{
    [DataContract]
    public class ClientSession
    {
        public string SessionId { get; set; }

        public string ClientId { get; set; } = "";

        /// <summary>
        /// 设备串码
        /// </summary>
        public string DeviceSerialNum { get; set; } = "";

        public string DBClientId { get; set; } = "";

        public string EnterpriseNum { get; set; } = "";

        public string LineNum { get; set; } = "";

        /// <summary>
        /// 注册时的经度
        /// </summary>
        public double BaseLng { get; set; }
        /// <summary>
        /// 注册时的纬度
        /// </summary>
        public double BaseLat { get; set; }

        public int OfflineWorkTime { get; set; }

        public int OfflineLockTime { get; set; }

        [Newtonsoft.Json.JsonIgnore]
        private DateTime LastReadClientMessageTime;

        [Newtonsoft.Json.JsonIgnore]
        private ReaderWriterLock RWL_LastReadClientMessageTime;

        [Newtonsoft.Json.JsonIgnore]
        public byte[] LastBytes;

        [Newtonsoft.Json.JsonIgnore]
        public Socket ClientSocket;

        [Newtonsoft.Json.JsonIgnore]
        private ReaderWriterLock RWL_ClientSocket;

        [Newtonsoft.Json.JsonIgnore]
        public byte[] RecvBuff;

        private int ClientState = 0;

        [Newtonsoft.Json.JsonIgnore]
        /// <summary>
        /// 参数为下线的设备ID
        /// </summary>
        public Action<string, string> OnClientDisposedEvent;

        public DateTime CreateTime;

        /// <summary>
        /// 客户端会话类,用于管理客户端连接和消息发送接收等操作.
        /// </summary>
        /// <param name="ClientSocket"></param>
        /// <param name="OnClientDisposedEvent"></param>
        public ClientSession(Socket ClientSocket, Action<string, string> OnClientDisposedEvent)
        {
            ClientId = "";
            CreateTime = DateTime.Now;
            LastReadClientMessageTime = DateTime.Now;
            LastBytes = new byte[0];
            RecvBuff = new byte[1024];
            RWL_ClientSocket = new ReaderWriterLock();
            RWL_LastReadClientMessageTime = new ReaderWriterLock();
            this.ClientSocket = ClientSocket;
            SessionId = Guid.NewGuid().ToString();
            this.OnClientDisposedEvent = OnClientDisposedEvent;
        }

        /// <summary>
        /// 获取客户端的远程端点信息,格式为"IP:xxx.xxx.xxx.xxx,Port:xxxx",如果发生异常,则返回空字符串.
        /// </summary>
        /// <returns></returns>
        public string GetClientRemoteEndPoint()
        {
            string Result = "";
            RWL_ClientSocket.AcquireWriterLock(-1);
            try
            {
                System.Net.IPEndPoint ipep = ClientSocket.RemoteEndPoint as System.Net.IPEndPoint;
                Result = "IP:" + ipep.Address.ToString() + ",Port:" + ipep.Port.ToString();
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex, $"SendClientMessage5:Result={Result} ");
            }
            RWL_ClientSocket.ReleaseWriterLock();
            return Result;
        }

        /// <summary>
        /// 设置最后一次接收客户端消息的时间,如果没有接收到过消息,则设置为当前时间.
        /// </summary>
        public void SetReceiveClientMessageTime()
        {
            RWL_LastReadClientMessageTime.AcquireWriterLock(-1);
            LastReadClientMessageTime = DateTime.Now;
            RWL_LastReadClientMessageTime.ReleaseWriterLock();
        }

        /// <summary>
        /// 获取最后一次接收客户端消息的时间间隔,单位为秒,如果没有接收到过消息,则返回0.
        /// </summary>
        /// <returns></returns>
        public int GetLastReceiveClientMessageTimespan()
        {
            RWL_LastReadClientMessageTime.AcquireWriterLock(-1);
            TimeSpan ts = DateTime.Now - LastReadClientMessageTime;
            RWL_LastReadClientMessageTime.ReleaseWriterLock();
            return (int)ts.TotalSeconds;
        }

        /// <summary>
        /// 获取最后一次接收客户端消息的时间,格式为"yyyy-MM-dd HH:mm:ss",如果没有接收到过消息,则返回当前时间.
        /// </summary>
        /// <returns></returns>
        public string GetLastReceiveClientMessageTime()
        {
            RWL_LastReadClientMessageTime.AcquireWriterLock(-1);
            string str = LastReadClientMessageTime.ToString("yyyy-MM-dd HH:mm:ss");
            RWL_LastReadClientMessageTime.ReleaseWriterLock();
            return str;
        }

        /// <summary>
        /// 发送消息到客户端,如果发生异常,则返回false,否则返回true.
        /// </summary>
        /// <param name="packet"></param>
        /// <returns></returns>
        public bool SendMessage(ServerMessagePacket packet)
        {
            bool Result = false;
            Exception exinfo = null;
            byte[] bsMessage = packet.ToBytes();
            LogHelper.Information($"SendClientMessage1:ClientId={ClientId},SessionId={SessionId}, CmdId={packet.Header.CommandId.ToString("X4")},Payload={packet.PayLoad}");
            RWL_ClientSocket.AcquireWriterLock(-1);
            try
            {
                var bb = ClientSocket.Send(bsMessage);
                if (bb > 0)
                    Result = true;
            }
            catch (Exception ex)
            {
                exinfo = ex;
            }
            RWL_ClientSocket.ReleaseWriterLock();
            if (!Result)
            {
                LogHelper.Error(exinfo, $"SendClientMessage2:ClientId={ClientId},SessionId={SessionId}, CmdId={packet.Header.CommandId.ToString("X4")}");
            }

            return Result;
        }

        /// <summary>
        /// 发送消息到客户端,并接收返回的消息,如果没有返回消息,则返回空字符串,如果发生异常,则返回空字符串.
        /// </summary>
        /// <param name="packet"></param>
        /// <returns></returns>
        public string SendReceiveMessage(ServerMessagePacket packet)
        {
            string Result = string.Empty;
            byte[] bsMessagep = packet.ToBytes();
            LogHelper.Information($"SendClientMessage3:ClientId={ClientId},SessionId={SessionId}, CmdId={packet.Header.CommandId.ToString("X4")},Payload={packet.PayLoad}");
            RWL_ClientSocket.AcquireWriterLock(-1);
            try
            {
                var b_send = ClientSocket.Send(bsMessagep);
                if (b_send > 0)
                {
                    byte[] bytes = new byte[1024];
                    int RecvSize = ClientSocket.Receive(bytes);
                    if (RecvSize == 0)
                        return Result;
                    SetReceiveClientMessageTime();
                    byte[] Total = new byte[RecvSize + LastBytes.Length];
                    LastBytes.CopyTo(Total, 0);
                    Array.Copy(RecvBuff, 0, Total, LastBytes.Length, RecvSize);
                    LastBytes = new byte[0];
                    RecvBuff.Initialize();
                    int index = 0;

                    string sPayLoad = string.Empty;
                    while (index < Total.Length)
                    {
                        int Remain = Total.Length - index;
                        if (Remain < 10)
                        {
                            LastBytes = new byte[Remain];
                            Array.Copy(Total, index, LastBytes, 0, Remain);
                            break;
                        }
                        Header head = null;
                        try
                        {
                            head = new Header(Total, index);
                        }
                        catch (Exception ex)
                        {
                            return Result;
                        }
                        if (Remain < head.MessageLength)
                        {
                            LastBytes = new byte[Remain];
                            Array.Copy(Total, index, LastBytes, 0, Remain);
                            break;
                        }
                        if (head.MessageLength == 0)
                        {
                            return Result;
                        }
                        byte[] bsMessage = new byte[head.MessageLength];
                        Array.Copy(Total, index, bsMessage, 0, bsMessage.Length);
                        index += head.MessageLength;
                        byte[] bsPayLoad = new byte[bsMessage.Length - IHeader.HeaderLength];
                        Array.Copy(bsMessage, IHeader.HeaderLength, bsPayLoad, 0, bsPayLoad.Length);
                        bsPayLoad = bsPayLoad.AES_Dencrypt();
                        if (bsPayLoad == null)
                        {
                            //解析失败了

                            return Result;
                        }
                        sPayLoad = bsPayLoad.UTF8ToString();
                        LogHelper.Information($"OnRecvClientLog,id={ClientId},payload={sPayLoad}");


                    }


                    LogHelper.Information(string.Format("Echo from server: {0}", sPayLoad));
                    return Result;

                }

            }
            catch (Exception ex)
            {
                LogHelper.Error(ex, $"SendClientMessage4:ClientId={ClientId},SessionId={SessionId}, CmdId={packet.Header.CommandId.ToString("X4")}");

            }
            RWL_ClientSocket.ReleaseWriterLock();

            return Result;
        }

        /// <summary>
        /// 发送消息到客户端,此函数只发送Header,不包含Payload数据,如果需要发送Payload数据,请使用SendMessage(ServerMessagePacket packet)函数.
        /// </summary>
        /// <param name="Header"></param>
        /// <returns></returns>
        public bool SendMessage(Header Header)
        {
            bool Result = false;
            Exception exinfo = null;
            byte[] bsMessage = Header.ToBytes();
            LogHelper.Information($"SendClientMessage5:ClientId={ClientId},SessionId={SessionId}, CmdId={Header.CommandId.ToString("X4")}");
            RWL_ClientSocket.AcquireWriterLock(-1);
            try
            {
                ClientSocket.Send(bsMessage);
                Result = true;
            }
            catch (Exception ex)
            {
                exinfo = ex;
            }
            RWL_ClientSocket.ReleaseWriterLock();
            if (!Result)
            {
                LogHelper.Error(exinfo, $"SendClientMessage6:ClientId={ClientId},SessionId={SessionId}, CmdId={Header.CommandId.ToString("X4")}");
            }
            return Result;
        }

        /// <summary>
        /// 获取Dispose状态,如果是第一次调用,则返回1,否则返回大于1的值.
        /// </summary>
        /// <returns></returns>
        private int GetDisposeState()
        {
            return Interlocked.Increment(ref ClientState);
        }

        /// <summary>
        /// Dispose客户端连接,并触发OnClientDisposedEvent事件
        /// </summary>
        /// <param name="DisposeType"></param>
        public void Dispose(int DisposeType = 0)
        {
            if (GetDisposeState() == 1)
            {

                try
                {
                    ClientSocket.Close();
                }
                catch (Exception ex)
                {
                    LogHelper.Error(ex, $"SendClienDisposetMessage:ClientId={ClientId},SessionId={SessionId}");
                }
                if (DisposeType == 0)
                {
                    if (OnClientDisposedEvent != null)
                    {
                        OnClientDisposedEvent(ClientId, DBClientId);
                    }
                }
            }

        }

        /// <summary>
        /// 开始接收客户端数据,并切调用RecvCallBack()函数,此函数也是个回掉函数,以此来实现每个客户端的多次数据发送.
        /// </summary>
        /// <param name="RecvCallBack"></param>
        /// <returns></returns>
        public bool BeginRecvData(AsyncCallback RecvCallBack)
        {
            bool Result = false;
            RWL_ClientSocket.AcquireWriterLock(-1);
            try
            {
                //开始接受客户端发过来的数据,RecvBuff,并切调用RecvCallBack()函数,此函数也是个回掉函数,以此来实现每个客户端的多次数据发送.
                ClientSocket.BeginReceive(RecvBuff, 0, RecvBuff.Length, SocketFlags.None, RecvCallBack, this);
                Result = true;
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex, $"SendClienDispsdosetMessage:ClientId={ClientId},SessionId={SessionId}");
            }
            finally
            {
                RWL_ClientSocket.ReleaseWriterLock();
            }
            return Result;
        }

        /// <summary>
        /// 结束接收客户端数据,并返回接收到的字节数,如果发生异常,则关闭客户端连接.
        /// </summary>
        /// <param name="ar"></param>
        /// <returns></returns>
        public int EndRecvData(IAsyncResult ar)
        {
            int RecvSize = 0;
            RWL_ClientSocket.AcquireWriterLock(-1);
            try
            {
                RecvSize = ClientSocket.EndReceive(ar);
            }
            catch (Exception ex)
            {
                try
                {
                    ClientSocket.Close();
                }
                catch (Exception exd)
                {
                    LogHelper.Error(exd, $"SendClienDispossdsetMessage:ClientId={ClientId},SessionId={SessionId}");
                }
                LogHelper.Error(ex, $"SendClienDiedsspossdsetMessage:ClientId={ClientId},SessionId={SessionId}");
            }
            finally
            {
                RWL_ClientSocket.ReleaseWriterLock();
            }
            return RecvSize;
        }

    }
}
